/**
 * 用来获取bezier上的点
 */
export class Point{
	/**
	 * 求线段AB上的任意位置的坐标
	 * @param x0 A点的x坐标
	 * @param y0 A点的y坐标
	 * @param x1 B点的x坐标
	 * @param y1 B点的y坐标
	 * @param ratio C点的位置比例值，即s = AC/AB
	 */
	static line(x0:number, y0:number, x1:number, y1:number, ratio:number):number[]{
		let k, b, x, y;
		if(x0 === x1){
			x = x0;
			y = ratio * (y1 - y0) + y0;
		}else{
			k = (y1 - y0) / (x1 - x0); //斜率
			b = y0 - k * x0;
			x = ratio * (x1 - x0) + x0;
			y = k * x + b;
		}
		return [x, y];
	}
	/**
	 * 获取系列控制点的(n阶)bezier曲线上的点
	 * @param points 控制点系列[x0,y0,x1,y1,x2,y2,,,,]，至少两个点，即length >=4
	 * @param ratio 位置比或时间比
	 */
	static get(points:number[], ratio: number, fn?:Function):number[]{
		let len:number = points.length, arr:number[], tmp:number[] = points;
		if(len%2) len--;
		while(len >= 4){
			arr = [];
			for(let i=0, point; i+2<len; i+=2){
				point = Point.line(tmp[i], tmp[i+1], tmp[i+2], tmp[i+3], ratio);
				arr.push(point[0], point[1]);
			}
			if(fn) fn(arr);
			len = arr.length;
			tmp = arr;
		}
		return arr;
	}
}

const fireAnimation = window.requestAnimationFrame || window.webkitRequestAnimationFrame;
const beziersCache:any = [];
let delta = 0;

function animate(time:number){
	delta = time - delta;
	beziersCache.forEach((b:Bezier) => {
		b.ticker(delta);
	});
	fireAnimation(animate);
	delta = time;
}
fireAnimation(animate);

export class Bezier{
	_delay:number = 0;
	_time:number = 0;
	_forward:number|boolean = 0;
	duration:number = 800;
	delay:number = 0;
	name:string|number[] = 'ease';
	count:number = 1;
	zoom:number = 100;
	forward:boolean = false;
	points:number[];
	curcount:number;
	autoplay:boolean = true;
	framerate:number;
	rate:number;
	state:string;
	constructor(options?:object){
		Object.assign(this, options);
		let writableNum = {
			writable: true,
			configurable: false,
			enumerable: false,
			value: 0
		}
		Object.defineProperties(this, {
			curcount: writableNum,
			framerate: {
				writable: true,
				configurable: false,
				enumerable: false,
				value: 60
			},
			rate: writableNum,
			state: {
				writable: true,
				configurable: false,
				enumerable: false,
				value: this.autoplay ? 'play' : 'stop'
			},
			points: {
				writable: true,
				configurable: false,
				enumerable: false,
				value: []
			},
			_time: writableNum,
			_delay: writableNum,
			_forward: writableNum
		});
		this.compute();
		this.onready();
		beziersCache.push(this);
	}
	compute(){
		let points:number[] = [];
		if(typeof this.name === 'string'){
			points.push(0, 0);
			switch(this.name){
				case 'ease':
					points.push(.25, .1, .25, 1);
					break;
				case 'ease-in':
					points.push(.42, 0);
					break;
				case 'ease-out':
					points.push(.58, 1);
					break;
				case 'ease-in-out':
					points.push(.42, 0, .58, 1);
					break;
			}
			points.push(1, 1);
			points.forEach((item, index)=> {
				points[index] = item * this.zoom;
			});
		}else{
			let i=0,len=this.name.length;
			if(len%2) len--;
			for(; i<len; i++){
				points[i] = this.name[i] * this.zoom;
			}
		}
		this.points = points;
		this.computed();
	}
	ticker(delta:number){
		if(this.state === 'play'){
			if(this._delay <= 0 && (this.count === 0 || this.curcount < this.count)){
				this.beforeUpdate();
				let arr = Point.get(this.points, this._time / this.duration, this.process);
				if(!arr) return;
				if(this._time < this.duration) {
					this.rate = this._forward ?  (1 - arr[1] / this.zoom) : (arr[1] / this.zoom);
					this.update(arr);
				}else{
					this.rate = this._forward ? 0 : 1;
					this.update([this.zoom, this.zoom]);
					this.curcount++;
					if(this.count === 0 || this.curcount < this.count){
						this.beforeNext();
					}else{
						this.ended();
						this.stop();
					}
					this._time = 0;
					this._forward = (this.forward && this.curcount%2 !== 0);
				}
				this._time += delta;
			}else{
				this._delay -= delta;
			}
		}
	}
	computed(){}
	onready(){}
	beforeUpdate(){}
	update(arr:number[]){}
	process(arr:number[]){}
	beforeNext(){}
	ended(){}
	start(){
		this.state = 'play';
	}
	stop(){
		this.curcount = 0;
		this.rate = 0;
		this._delay = this.delay;
		this.state = 'stop';
		this._time = 0;
		this._forward = 0;
	}
	pause(){
		this.state = 'pause';
	}
	destroy(){
		for(let i=0, len = beziersCache.length; i<len; i++){
			if(beziersCache[i] === this) {
				beziersCache.splice(i, 1);
				break;
			}
		}
	}
}

// 数学公式B(t) = Pi(1-t)^(n-i)  *  t^i

